﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Script.Serialization;

namespace iMobileS.Common
{
    public static class JsonToObject
    {
        /// <summary>
        /// Serialize an object to JSON string
        /// </summary>
        /// <param name="obj">Object to convert</param>
        /// <returns></returns>
        public static string ToJson(object obj)
        {
            if (obj == null)
                return "{}";
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(obj);
        }
    }
    public static class JsonToObject<T> where T : class
    {
        private static DateTime BASE_DATE = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        /// <summary>
        /// Serialize an object to JSON string
        /// </summary>
        /// <param name="obj">Object to convert</param>
        /// <returns></returns>
        public static string ToJson(object obj)
        {
            if (obj == null)
                return "{}";
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(obj);
        }

        /// <summary>
        /// Deserialize a Json string to <para>T</para> object. If object has any DateTime value, it's automatically converted to UTC-0 timezone
        /// </summary>
        /// <param name="jsonObjectString">Json string</param>
        /// <returns>Return <para>T</para> object. NULL if deserialize fail</returns>
        public static T ToObject(string jsonObjectString)
        {
            return ToObject(jsonObjectString, null);
        }
        /// <summary>
        /// Deserialize a Json string to <para>T</para> object. If object has any DateTime value, it's automatically converted to UTC-0 timezone
        /// </summary>
        /// <param name="jsonObjectString">Json string</param>
        /// <param name="converters">List of JavaScriptConverter</param>
        /// <returns>Return <para>T</para> object. NULL if deserialize fail</returns>
        public static T ToObject(string jsonObjectString, IEnumerable<JavaScriptConverter> converters)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();

            if (converters != null)
            {
                serializer.RegisterConverters(converters);
            }
            T obj = serializer.Deserialize<T>(jsonObjectString);
            ToUniversalTime(obj);
            return obj;
        }


        /// <summary>
        /// Deserialize a list of objects in Json string to List of <para>T</para> objects.
        /// If list has any DateTime value, it's automatically converted to UTC-0 timezone
        /// </summary>
        /// <param name="listJsonObjectString">Json string</param>
        /// <returns></returns>
        public static List<T> ToList(string listJsonObjectString)
        {
            return ToList(listJsonObjectString, null);
        }
        /// <summary>
        /// Deserialize a list of objects in Json string to List of <para>T</para> objects
        /// </summary>
        /// <param name="listJsonObjectString">Json string</param>
        /// <returns></returns>
        public static List<T> ToList(string listJsonObjectString, IEnumerable<JavaScriptConverter> converters)
        {
            if (String.IsNullOrEmpty(listJsonObjectString))
                return new List<T>();

            JavaScriptSerializer serializer = new JavaScriptSerializer();

            if (converters != null)
            {
                serializer.RegisterConverters(converters);
            }

            List<T> listObject = serializer.Deserialize<List<T>>(listJsonObjectString);
            ToUniversalTime(listObject);
            return listObject;
        }

        private static Func<DateTime, DateTime> toUTC =
            actualTime =>
            {
                // Current time of page (with page's timezone)
                DateTime cPageTime = DateTime.UtcNow;//.AddMinutes(Facade.Facade.Current.Context.TimeZone * 60);

                // Date which is parsed from client's date
                DateTime temp = new DateTime(
                    actualTime.Year,
                    actualTime.Month,
                    actualTime.Day,
                    cPageTime.Hour,
                    cPageTime.Minute,
                    cPageTime.Second,
                    cPageTime.Millisecond,
                    DateTimeKind.Unspecified
                );

                // Convert to UTC-0
                temp = temp.ToUniversalTime();// AddMinutes(0 - (Facade.Facade.Current.Context.TimeZone * 60));
                return temp;
            };

        /// <summary>
        /// Convert all DateTime value (which are passed from client) to UTC-0 timezone
        /// </summary>
        /// <param name="t"></param>
        private static void ToUniversalTime(object t)
        {

            if (t == null)
                return;

            Type type = t.GetType();
            if (typeof(IList).IsAssignableFrom(type))
            {
                foreach (object obj in (IList)t)
                {
                    ToUniversalTime(obj);
                }
            }
            else if (type == typeof(DateTime))
            {
                t = toUTC((DateTime)t);
            }
            else if (type == typeof(DateTime?))
            {
                DateTime? tmp = t as DateTime?;
                if (tmp != null && tmp.HasValue)
                {
                    t = toUTC(tmp.Value);
                }
            }
            else if (type.IsClass)
            {
                if (!type.IsValueType && type != typeof(String))
                {
                    type
                        .GetProperties()
                        .Where(p => p.PropertyType == typeof(DateTime)
                            || p.PropertyType == typeof(DateTime?) // type of System.Nullable<DateTime>
                            || (!p.PropertyType.IsValueType && p.PropertyType != typeof(String)))
                        .Each(p =>
                        {
                            if (p.PropertyType == typeof(DateTime))
                            {
                                p.SetValue(t, toUTC((DateTime)p.GetValue(t, null)), null);
                            }
                            else if (p.PropertyType == typeof(DateTime?))
                            {
                                DateTime? tmp = p.GetValue(t, null) as DateTime?;

                                if (tmp != null && tmp.HasValue)
                                {
                                    p.SetValue(t, toUTC(tmp.Value), null);
                                }
                            }
                            else
                            {
                                ToUniversalTime(p.GetValue(t, null));
                            }
                        });
                }
            }
        }
    }
}